package com.medvalley.tcm.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.medvalley.tcm.base.BusinessException;
import com.medvalley.tcm.dao.AppointmentDao;
import com.medvalley.tcm.dao.OrgDao;
import com.medvalley.tcm.dao.OrgStaffDao;
import com.medvalley.tcm.dao.SickInfoStorageDao;
import com.medvalley.tcm.domain.Appointment;
import com.medvalley.tcm.domain.OrgStaff;
import com.medvalley.tcm.domain.Organization;
import com.medvalley.tcm.domain.SickInfoStorage;
import com.medvalley.tcm.vo.inputvo.AppointmentInputVo;
import com.medvalley.tcm.vo.inputvo.OrgStaffLoginInputVo;
import com.medvalley.tcm.vo.outputvo.AppointmentOutputVo;
import com.medvalley.tcm.vo.outputvo.OrgStaffOutputVo;
import com.medvalley.tcm.vo.outputvo.StaffAppointmentListOutputVo;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class AppointmentService {

    private static Logger log = LoggerFactory.getLogger(AppointmentService.class);

    @Autowired
    private OrgStaffDao orgStaffDao;

    @Autowired
    private AppointmentDao appointmentDao;

    @Autowired
    private SickInfoStorageDao sickInfoStorageDao;

    @Autowired
    private OrgDao orgDao;

    private static Cache<String, String> appointmentCache = CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.SECONDS)
            .build();


    public OrgStaffOutputVo checkStaff(String staffName, String staffNo, int orgId) {
        OrgStaff orgStaff = orgStaffDao.findOneByStaffNameAndStaffNoAndOrgId(staffName, staffNo, orgId);
        if (orgStaff == null) {
            throw new BusinessException("没有此员工");
        }
        OrgStaffOutputVo orgStaffOutputVo = new OrgStaffOutputVo();
        orgStaffOutputVo.setId(orgStaff.getId());
        orgStaffOutputVo.setOrgId(orgId);
        orgStaffOutputVo.setStaffName(orgStaff.getStaffName());
        return orgStaffOutputVo;
    }
    public OrgStaffOutputVo checkStaffV2( String staffNo, int orgId) {
        OrgStaff orgStaff = orgStaffDao.findOneByStaffNoAndOrgId(staffNo, orgId);
        if (orgStaff == null) {
            throw new BusinessException("没有此员工");
        }
        OrgStaffOutputVo orgStaffOutputVo = new OrgStaffOutputVo();
        orgStaffOutputVo.setId(orgStaff.getId());
        orgStaffOutputVo.setOrgId(orgId);
        orgStaffOutputVo.setStaffName(orgStaff.getStaffName());
        return orgStaffOutputVo;
    }
    public OrgStaffOutputVo staffLogin(OrgStaffLoginInputVo orgStaffLoginInputVo) {
        OrgStaff orgStaff = orgStaffDao.findOneByStaffNoAndOrgIdAndStaffPhone(orgStaffLoginInputVo.getStaffNo(),
                orgStaffLoginInputVo.getOrgId(), orgStaffLoginInputVo.getStaffPhone());
        if (orgStaff == null) {
            throw new BusinessException("您的工号或手机号输入有误。");
        }
        OrgStaffOutputVo orgStaffOutputVo = new OrgStaffOutputVo();
        orgStaffOutputVo.setId(orgStaff.getId());
        orgStaffOutputVo.setOrgId(orgStaffLoginInputVo.getOrgId());
        orgStaffOutputVo.setStaffName(orgStaff.getStaffName());
        return orgStaffOutputVo;
    }

    public AppointmentOutputVo doAppointment(AppointmentInputVo appointmentInputVo) {
        AppointmentOutputVo appointmentOutputVo = new AppointmentOutputVo();
        String phone = appointmentInputVo.getSickPhone();
        if (appointmentCache.getIfPresent(phone) != null) {
            throw new BusinessException("请勿重复操作");
        }
        appointmentCache.put(phone, phone);
        Appointment appointment = appointmentDao.findOneBySickPhoneAndStatus(phone, 0);
        if (appointment != null && !isExpired(appointment)) {
            BeanUtils.copyProperties(appointment, appointmentOutputVo);
            appointmentOutputVo.setIsHaveAppointment(1);
            return appointmentOutputVo;
        }
        Organization org = orgDao.findById(appointmentInputVo.getOrgId()).orElseThrow(() -> new BusinessException("机构不存在"));
        appointment = new Appointment();
        appointment.setSickName(appointmentInputVo.getSickName());
        String sickBirthday = appointmentInputVo.getSickBirthday();
        if (!StringUtils.isEmpty(sickBirthday)) {
            appointment.setSickBirthday(sickBirthday);
            try {
                int age = getAgeByBirth(DateUtils.parseDate(sickBirthday, "yyyy-MM-dd"));
                appointment.setSickAge(age);
            } catch (ParseException e) {
                log.warn(" parse birthday {} error", sickBirthday);
                appointment.setSickAge(0);
            }
        } else {
            log.warn(" birthday is empty, input:{}", JSON.toJSONString(appointmentInputVo));
        }
        appointment.setSickGender(appointmentInputVo.getSickGender());
        appointment.setSickPhone(appointmentInputVo.getSickPhone());
        appointment.setCheckerId(appointmentInputVo.getCheckerId());
        appointment.setStatus(0);
        appointment.setOrgName(org.getName());
        appointment.setAppointmentDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"));
        appointment.setBarcode(this.generateBarcode());
        appointment.setCreateTime(new Date());
        appointmentDao.save(appointment);
        BeanUtils.copyProperties(appointment, appointmentOutputVo);
        appointmentOutputVo.setIsHaveAppointment(0);
        return appointmentOutputVo;
    }


    public StaffAppointmentListOutputVo listAppointment(long checkerId) {
        StaffAppointmentListOutputVo staffAppointmentListOutputVo = new StaffAppointmentListOutputVo();
        List<AppointmentOutputVo> appointmentOutputVoList = Lists.newArrayList();
        staffAppointmentListOutputVo.setSickList(appointmentOutputVoList);
        staffAppointmentListOutputVo.setStaffId(checkerId);
        List<Appointment> appointmentList = appointmentDao.findAllByCheckerId(checkerId);
        if (!CollectionUtils.isEmpty(appointmentList)) {
            List<String> barcodeList = appointmentList.stream().map(Appointment::getBarcode).collect(Collectors.toList());
            List<SickInfoStorage> sickInfoStorages = sickInfoStorageDao.findAllByBarCodeIn(barcodeList);
            Map<String, SickInfoStorage> barcodeStorage = Maps.newHashMap();
            if (!CollectionUtils.isEmpty(sickInfoStorages)) {
                barcodeStorage = sickInfoStorages.stream().collect(Collectors.toMap(SickInfoStorage::getBarCode, Function.identity()));
            }
            for (int i = 0; i < appointmentList.size(); i++) {
                Appointment appointment = appointmentList.get(i);
                AppointmentOutputVo appointmentOutputVo = new AppointmentOutputVo();
                appointmentOutputVo.setBarcode(appointment.getBarcode());
                appointmentOutputVo.setAppointmentDate(appointment.getAppointmentDate());
                appointmentOutputVo.setSickName(appointment.getSickName());
                appointmentOutputVo.setSickAge(appointment.getSickAge());
                appointmentOutputVo.setSickGender(appointment.getSickGender());
                if (barcodeStorage.get(appointment.getBarcode()) != null) {
                    appointmentOutputVo.setDownloadUrl(barcodeStorage.get(appointment.getBarcode()).getFilePath());
                }
                appointmentOutputVo.setStatus(appointment.getStatus());
                appointmentOutputVoList.add(appointmentOutputVo);
            }
        }

        return staffAppointmentListOutputVo;

    }

    public AppointmentOutputVo getAppointmentInfo(String barcode) {
        Appointment appointment = appointmentDao.findOneByBarcode(barcode);
        if (appointment == null) {
            throw new BusinessException("未查到预约信息");
        }
        if (isExpired(appointment)) {
            throw new BusinessException("条形码已失效");
        }

        AppointmentOutputVo appointmentOutputVo = new AppointmentOutputVo();
        appointmentOutputVo.setSickName(appointment.getSickName());
        appointmentOutputVo.setSickGender(appointment.getSickGender());
        appointmentOutputVo.setSickPhone(appointment.getSickPhone());
        appointmentOutputVo.setOrgName(appointment.getOrgName());
        appointmentOutputVo.setSickAge(appointment.getSickAge());
        appointmentOutputVo.setStatus(appointment.getStatus());
        appointmentOutputVo.setSickBirthday(appointment.getSickBirthday());
        appointmentOutputVo.setAppointmentDate(appointment.getAppointmentDate());
        return appointmentOutputVo;
    }

    public boolean checkBarcodeExpire(String barcode) {

        Appointment appointment = appointmentDao.findOneByBarcode(barcode);
        if (appointment == null) {
            log.info("barcode:{} status is {}", barcode, JSON.toJSONString(appointment));
            throw new BusinessException("条形码无效");
        }

        return isExpired(appointment);
    }

    private boolean isExpired(Appointment appointment) {

        if (2 == appointment.getStatus()) {
            return true;
        }
        Date createTime = appointment.getCreateTime();
        Date expireDate = DateUtils.addHours(createTime, 336);
        Date now = new Date();
        boolean isExpired = now.compareTo(expireDate) == 1;
        if (isExpired) {
            appointment.setStatus(2);
            appointmentDao.save(appointment);
        }
        return isExpired;
    }

    private String generateBarcode() {
        StringBuilder barcodeBuilder = new StringBuilder("hyg");
        String time = DateFormatUtils.format(new Date(), "yyMMddHHmmssS");
        barcodeBuilder.append(time);
        return barcodeBuilder.toString();
    }

    private int getAgeByBirth(Date birthday) {
        int age = 0;
        try {
            Calendar now = Calendar.getInstance();
            now.setTime(new Date());// 当前时间

            Calendar birth = Calendar.getInstance();
            birth.setTime(birthday);

            if (birth.after(now)) {//如果传入的时间，在当前时间的后面，返回0岁
                age = 0;
            } else {
                age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
                if (now.get(Calendar.DAY_OF_YEAR) > birth.get(Calendar.DAY_OF_YEAR)) {
                    age += 1;
                }
            }
            return age;
        } catch (Exception e) {
            return 0;
        }
    }

}
